今天要介紹另一個 hooks - useReducer。
光看第一行有些讀者可能就會有一些疑問了,udeReducer?看起來跟 redux 的 reducer 有 87分像欸!
沒錯,是真的在用途上滿像的,這也引起許多關於取代 redux 的爭論。
首先我們來看看官方怎麼描述這個新方法
是 useState 的另一種替代。它接受(state, action) => newState,并且返回了一个與當前state成對的dispatch的方法。(如果你熟悉 Redux ,你也很快就能理解它是怎麼工作的。)useState 實際上執行的也是一個 useReducer。這意味著 useReducer 是更原生的,你能在任何使用 useState 的地方都替换成使用 useReducer。
我們來看看最簡單的用法,依然以 counter 為範例:
import React, { useReducer } from 'react';
const initialState = 0;
const reducer = (state, action) => {
switch (action) {
case 'increment': return state + 1;
case 'decrement': return state - 1;
case 'reset': return 0;
default: return state;
}
};
const Counter = () => {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
{count}
<button onClick={() => dispatch('increment')}>+1</button>
<button onClick={() => dispatch('decrement')}>-1</button>
<button onClick={() => dispatch('reset')}>reset</button>
</div>
);
};
export default Counter;
對比 redux 的 reducer,可能有讀者會發現這邊的 state 是一個數字而不是之前 redux reducer 使用的物件。
當然, state 要是一個物件也是可以的:
const initialState = {
count_1: 0,
count_2: 0,
};
const reducer = (state, action) => {
switch (action.type) {
case 'increment_1':
return { ...state, count1: state.count_1 + 1 };
case 'decrement_1':
return { ...state, count1: state.count_1 - 1 };
case 'set1':
return { ...state, count1: action.count };
case 'increment_2':
return { ...state, count2: state.count_2 + 1 };
case 'decrement_2':
return { ...state, count2: state.count_2 - 1 };
case 'set2':
return { ...state, count2: action.count };
default:
return state;
}
};
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<div>
{state.count_1}
<button onClick={() => dispatch({ type: 'increment_1' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement_1' })}>-1</button>
<button onClick={() => dispatch({ type: 'set1', count: 0 })}>reset</button>
</div>
<div>
{state.count_2}
<button onClick={() => dispatch({ type: 'increment_2' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement_2' })}>-1</button>
<button onClick={() => dispatch({ type: 'set2', count: 0 })}>reset</button>
</div>
</>
);
};
const initialState = 0;
const reducer = (state, action) => {
switch (action.type) {
case 'increment': return state + 1;
case 'decrement': return state - 1;
case 'set': return action.count;
default: return state;
}
};
const Counter = () => {
const [counter1, dispatch_1] = useReducer(reducer, initialState);
const [counter2, dispatch_2] = useReducer(reducer, initialState);
return (
<>
<div>
{counter1}
<button onClick={() => dispatch_1({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch_1({ type: 'decrement' })}>-1</button>
<button onClick={() => dispatch_1({ type: 'set', count: 0 })}>reset</button>
</div>
<div>
{counter2}
<button onClick={() => dispatch_2({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch_2({ type: 'decrement' })}>-1</button>
<button onClick={() => dispatch_2({ type: 'set', count: 0 })}>reset</button>
</div>
</>
);
};
特別注意,action 是一個物件,state 是單純的數字。
counter1 與 counter2 擁有各自的 dispatch 方法,卻是共用同一個 reducer。
看到這你可能會說:就算 usereducer 可以取代 redux 的 reducer 好了,我還是得依靠 redux 擁有 global 的 store 啊!
別急,單靠 useReducer 的確不夠,明天將介紹 contextAPI 與 useReducer 做結合來實現 redux 的功能,到時候可能真的就不一定需要 redux 了呢!(不一定而已)